library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.typeDefinitions.all;

entity instDecode is
  
  port (
    instr    : in  std_logic_vector(31 downto 0);  --done
    equal    : in  std_logic;
    rs       : out std_logic_vector(4 downto 0);   --done
    rt       : out std_logic_vector(4 downto 0);   --done
    rd       : out std_logic_vector(4 downto 0);   --done
    shftamnt : out std_logic_vector(4 downto 0);   --done
    imm16    : out std_logic_vector(15 downto 0);  --done
    aluOp    : out std_logic_vector(3 downto 0);   --done
    aluSrc   : out std_logic_vector(1 downto 0);
    memToReg : out std_logic_vector(1 downto 0);   --done
    extImm   : out std_logic;                      --done
    memW     : out std_logic;                      --done
    waitMem  : out std_logic;                      --done
    regW     : out std_logic;                      --done
    uns      : out std_logic;                      --done
    regDest  : out std_logic_vector(1 downto 0);
    pcSel    : out std_logic_vector(1 downto 0));  --done


end instDecode;

architecture arch of instDecode is
  signal op     : operation;
  signal opcode : std_logic_vector(5 downto 0);
  signal fnct   : std_logic_vector(5 downto 0);
begin

  pc : process (op, equal)
  begin  -- process pc
    case op is
      when o_jr =>
        pcSel <= "11";
      when o_j | o_jal =>
        pcSel <= "10";
      when o_beq =>
        if(equal = '1') then
          pcSel <= "01";
        else
          pcSel <= "00";
        end if;
      when o_bne =>
        if(equal = '0') then
          pcSel <= "01";
        else
          pcSel <= "00";
        end if;
        
      when others =>
        pcSel <= "00";
    end case;
  end process pc;

  aluS : process (op)
  begin  -- 1 = immediate 0 = bus B
    case op is
      when o_sll | o_srl =>
        aluSrc <= "10";
      when o_addiu | o_andi | o_ori | o_slti | o_sltiu | o_xori | o_sw | o_ll | o_lw =>
        aluSrc <= "01";
      when others =>
        aluSrc <= "00";
    end case;
  end process aluS;

  alu : process (op)
  begin  -- process alu
    case op is
      when o_sltu | o_sltiu | o_slt | o_slti =>
        aluOp <= "1000";
      when o_sll =>
        aluOp <= "0000";
      when o_srl =>
        aluOp <= "0001";
      when o_addu | o_addiu | o_lw | o_sw =>  -- add register plus offest
        aluOp <= "0010";
      when o_subu =>
        aluOp <= "0011";
      when o_and | o_andi =>
        aluOp <= "0100";
      when o_nor =>
        aluOp <= "0101";
      when o_or | o_ori =>
        aluOp <= "0110";
      when o_xor | o_xori =>
        aluOp <= "0111";
      when others =>
        aluOp <= "0000";
    end case;
  end process alu;

  unsign : process (op)
  begin  -- process unsign
    case op is
      when o_sltu | o_sltiu | o_addu | o_subu | o_addiu =>
        uns <= '1';
      when others =>
        uns <= '0';
    end case;
  end process unsign;

  memory : process (op)
  begin  -- process memToReg 0=ALU 1=DataMem 2=shifted imm 3=PC+8?
    case op is
      when o_lui =>
        memToReg <= "10";
      when o_ll | o_lw =>
        memToReg <= "01";
      when o_jal =>
        memToReg <= "11";
      when others =>                    --rt
        memToReg <= "00";
    end case;
  end process memory;

  extension : process (op)
  begin  -- process extImm ... 1 = sign extend
    case op is
      when o_xori | o_ori | o_andi =>   --31
        extImm <= '0';
      when others =>                    --rt
        extImm <= '1';
    end case;
  end process extension;

  destination : process (op)
  begin  -- process destination
    case op is
      when o_jal =>                                          --31
        regDest <= "00";
      when o_addu | o_and | o_nor | o_or | o_slt | o_sltu |  --rd
        o_sll | o_srl | o_subu | o_xor =>
        regDest <= "10";
      when others =>                                         --rt
        regDest <= "01";
    end case;
  end process destination;

  stop : process (op)
  begin  -- process stop
    case op is
      when o_lw =>
        waitMem <= '1';
      when others =>
        waitMem <= '0';
    end case;
  end process stop;

  regWrite : process (op)
  begin  -- process regW
    case op is
      when o_jr | o_beq | o_bne | o_sw | o_ll | o_sc =>
        regW <= '0';
      when others =>
        regW <= '1';
    end case;
  end process regWrite;

  memWrite : process (op)
  begin  -- process memW
    case op is
      when o_sw =>                      --not sure if there are more here!!!
        memW <= '1';
      when others =>
        memW <= '0';
    end case;
  end process memWrite;

  decode : process (instr, opcode, fnct)
  begin  -- process decode
    op <= o_addu;
    case TO_INTEGER(unsigned(opcode)) is
      when 0 =>
        case TO_INTEGER(unsigned(fnct)) is
          when 0 =>
            op <= o_sll;
          when 2 =>
            op <= o_srl;
          when 8 =>
            op <= o_jr;
          when 33 =>
            op <= o_addu;
          when 35 =>
            op <= o_subu;
          when 36 =>
            op <= o_and;
          when 37 =>
            op <= o_or;
          when 38 =>
            op <= o_xor;
          when 39 =>
            op <= o_nor;
          when 42 =>
            op <= o_slt;
          when 43 =>
            op <= o_sltu;
          when others =>
            op <= o_halt;  --hope we never hit this. Unsupported op
        end case;
      when 2 =>
        op <= o_j;
      when 3 =>
        op <= o_jal;
      when 4 =>
        op <= o_beq;
      when 5 =>
        op <= o_bne;
      when 9 =>
        op <= o_addiu;
      when 10 =>
        op <= o_slti;
      when 11 =>
        op <= o_sltiu;
      when 12 =>
        op <= o_andi;
      when 13 =>
        op <= o_ori;
      when 14 =>
        op <= o_xori;
      when 15 =>
        op <= o_lui;
      when 35 =>
        op <= o_lw;
      when 43 =>
        op <= o_sw;
      when 48 =>
        op <= o_ll;
      when 56 =>
        op <= o_sc;
      when others =>
        op <= o_halt;                   --invalid op
    end case;
    
  end process decode;

  rs       <= instr(25 downto 21);
  rt       <= instr(20 downto 16);
  rd       <= instr(15 downto 11);
  imm16    <= instr(15 downto 0);
  opcode   <= instr(31 downto 26);
  fnct     <= instr(5 downto 0);
  shftamnt <= instr(10 downto 6);
  
end arch;
